﻿using System;
using System.Linq;
using Microsoft.WindowsAzure.ServiceRuntime;
using WcfMasterSlaveElectionService;

namespace WcfActivePassiveWorkerRole
{
    /// <summary>
    ///  Base class deriving from RoleEntryPoint. Derive a worker role from the ActivePassiveWorkerRole
    /// to add the Master/Slave (or active / passive) behavior to the worker role.
    /// </summary>
    public abstract class ActivePassiveWorkerRole : RoleEntryPoint
    {
        /// <summary>
        /// WCF host for master slave election
        /// </summary>
        protected MasterElectionService MasterElector;
        /// <summary>
        /// Master func thread: the actual work of the master
        /// </summary>
        protected AzureEndpointProvider EndpointProvider;
        protected bool IAmMaster = false;

        /// <summary>
        /// Must override to stop the behaving as the master and become a slave
        /// </summary>
        protected abstract void ChangeToSlaveMode();
        /// <summary>
        /// Must override to stop the behaving as a slave and become the master
        /// </summary>
        protected abstract void ChangeToMasterMode();

        protected virtual void InstantiateMasterElectionService(ILog logger)
        {
            // Instantiates the endpoint provider. Since we are running in azure, use the AzureEnpointProvider
            EndpointProvider = new AzureEndpointProvider(logger);

            //instantiates the master election service 
            MasterElector = new MasterElectionService(RoleEnvironment.CurrentRoleInstance.Id, EndpointProvider, logger);

            // Subscribe to the NewMaster event
            MasterElector.NewMaster += new EventHandler<MasterSelectionEventArgs>(MasterElectorNewMaster);
            MasterElector.ExpectRole += new EventHandler<ExpectMasterEventArgs>(MasterElectorExpectRole);

            //Instantiates and starts the instance manager server
            //Listen on master election service
            MasterElector.StartListen();

            // Subscribe to RoleEnvironment_Changed to reload endpoints
            RoleEnvironment.Changed += new EventHandler<RoleEnvironmentChangedEventArgs>(RoleEnvironmentChanged);
            EndpointProvider.OnConfigurationChanged();

            //This method is called to start and ensure the election of a master
            MasterElector.EnsureMasterElected();

        }

        /// <summary>
        /// Role instances changed. Need to refresh the list of services participating in the master/slave election
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void RoleEnvironmentChanged(object sender, RoleEnvironmentChangedEventArgs e)
        {
            if (e.Changes.Any(change => change is RoleEnvironmentTopologyChange))
            {
                EndpointProvider.OnConfigurationChanged();
            }
        }

        /// <summary>
        /// Called by the watchdog. The watchdog lets us know what it expect us to be: master or slave
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MasterElectorExpectRole(object sender, ExpectMasterEventArgs e)
        {
            OnMasterElectorExpectRole(e.ExpectedRoleIsMaster);
        }

        /// <summary>
        /// On election of new master, this instance is notified. This is our trigger
        /// to change to master or slave mode.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void MasterElectorNewMaster(object sender, MasterSelectionEventArgs e)
        {
            if (MasterElector.IsMaster())
            {
                IAmMaster = true;
                ChangeToMasterMode();
            }
            else
            {
                IAmMaster = false;
                ChangeToSlaveMode();
            }
        }

        /// <summary>
        /// Check if the role is in the same state as the MasterElectionService expects it to be.
        /// </summary>
        /// <param name="expectMaster"></param>
        protected virtual void OnMasterElectorExpectRole(bool expectMaster)
        {
            if (expectMaster != IAmMaster)
            {
                RoleEnvironment.RequestRecycle();
            }
        }
        
    }
}
